#!/bin/bash
#该脚本为Linux下启动java程序的通用脚本。即可以作为开机自启动service脚本被调用，
#也可以作为启动java程序的独立脚本来使用。
#
#Author: DuanYong(www.javacoo.com)
#Date: 2017/12/25
#
#警告!!!：该脚本stop部分使用系统kill命令来强制终止指定的java程序进程。
#在杀死进程前，未作任何条件检查。在某些情况下，如程序正在进行文件或数据库写操作，
#可能会造成数据丢失或数据不完整。如果必须要考虑到这类情况，则需要改写此脚本，
#增加在执行kill命令前的一系列检查。
#使用方法：
#1.启动所有jar程序：sh launcher_all.sh start all
#2.停止所有jar程序：sh launcher_all.sh stop all
#3.重启所有jar程序：sh launcher_all.sh restart all
#4.单独启动、停止、重启某个jar程序：把最后面的all替换为某个jar程序的代码即可
#
#
###################################
#应用程序所在的目录
APP_HOME=/home/chdev/opt/xservice
#应用日志存放目录
APP_LOG_PATH=/data/tomcat/logs
#java虚拟机启动参数-案例8188
JAVA_OPTS_LAUNCHER_DATA_8188="-server -ms2048m -mx2048m -XX:+UseG1GC -XX:G1HeapRegionSize=2m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintHeapAtGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$APP_LOG_PATH/launcher_launcher_data_v4/dump -Xloggc:$APP_LOG_PATH/launcher_launcher_data_v4/gc/gc_8188.log -Djava.awt.headless=true"

#应用工程名称数组
PROJECT_APP_NAMES=(launcher_data_8188)
#应用jar包名称数组
PROJECT_JARS=(xservice_example-1.0.0)
#应用名称数组
PROJECT_NAMES=(launcher主场景数据8188)
#应用java虚拟机启动参数数组
PROJECT_JAVA_OPTS=("$JAVA_OPTS_LAUNCHER_DATA_8188")
#应用开启端口参数数组
PROJECT_PORTS=(8188)

#命令
PROJECT_CMD="all|launcher_data_8188"

###################################
#(函数)判断程序是否已启动
#
#说明：
#使用JDK自带的JPS命令及grep命令组合，准确查找pid
#jps 加 l 参数，表示显示java的完整包路径
#使用awk，分割出pid ($1部分)，及Java程序名称($2部分)
###################################
 
checkpid() {
   local psid=0
   javaps=`$JAVA_HOME/bin/jps -l | grep $1`
 
   if [ -n "$javaps" ]; then
      psid=`echo $javaps | awk '{print $1}'`
   else
      psid=0
   fi
   echo $psid;
}
 
###################################
#(函数)启动程序
#
#说明：
#1. 首先调用checkpid函数
#2. 如果程序已经启动（$psid不等于0），则提示程序已启动
#3. 如果程序没有被启动，则执行启动命令行
#4. 启动命令执行后，再次调用checkpid函数
#5. 如果步骤4的结果能够确认程序的pid,则打印[OK]，否则打印[Failed]
#注意：echo -n 表示打印字符后，不换行
#注意: "nohup 某命令 >/dev/null 2>&1 &" 的用法
###################################
start() {
	local APPNAME=
	local NAME=
	local CLASSNAME=
	local JAVA_OPTS=
	local PORT=
	local cmd2="$1"
    local psid=0
	local okcount=0
	local cmd2ok=0
	echo "===============开始启动服务..."
	for(( i=0;i<${#PROJECT_APP_NAMES[@]};i++))
		do
			APPNAME=${PROJECT_APP_NAMES[$i]}
			NAME=${PROJECT_NAMES[$i]}
			CLASSNAME=${PROJECT_JARS[$i]}
			JAVA_OPTS=${PROJECT_JAVA_OPTS[$i]}
			PORT=${PROJECT_PORTS[$i]}
			if [ "$cmd2" == "all" ] || [ "$cmd2" == "$APPNAME" ]; then
				cmd2ok=1
			   psid=$(checkpid $CLASSNAME);
			   
			   if [ $psid -ne 0 ]; then
			      echo "================================"
			      echo "warn: $CLASSNAME already started! (pid=$psid)"
			      echo "================================"
			   else
			      echo -n "Starting $CLASSNAME ..."
			      nohup java $JAVA_OPTS -jar $APP_HOME/"$CLASSNAME".jar --spring.profiles.active=testdev --server.port=$PORT  >/dev/null 2>&1 &
			      psid=$(checkpid $CLASSNAME);
			      if [ $psid -ne 0 ]; then
			         okcount=$(($okcount+1))
			         echo "(pid=$psid,port=$PORT) [OK]"
			      else
			         echo "[Failed]"
			      fi
			   fi
		  fi   
		done
		if (($cmd2ok==0)) 
		then
			echo "command2: $PROJECT_CMD"
		else
			echo "---------------------------本次启动:$okcount个服务"
		fi
}
 
###################################
#(函数)停止程序
#
#说明：
#1. 首先调用checkpid函数
#2. 如果程序已经启动（$psid不等于0），则开始执行停止，否则，提示程序未运行
#3. 使用kill -9 pid命令进行强制杀死进程
#4. 执行kill命令行紧接其后，马上查看上一句命令的返回值: $?
#5. 如果步骤4的结果$?等于0,则打印[OK]，否则打印[Failed]
#6. 为了防止java程序被启动多次，这里增加反复检查进程，反复杀死的处理（递归调用stop）。
#注意：echo -n 表示打印字符后，不换行
#注意: 在shell编程中，"$?" 表示上一句命令或者一个函数的返回值
###################################
stop() {
	local APPNAME=
	local NAME=
	local CLASSNAME=
	local PORT=
	local cmd2="$1"
    local psid=0
	local okcount=0
	local cmd2ok=0
	echo "===============开始停止服务..."
	for(( i=0;i<${#PROJECT_APP_NAMES[@]};i++))
		do
			APPNAME=${PROJECT_APP_NAMES[$i]}
			NAME=${PROJECT_NAMES[$i]}
			CLASSNAME=${PROJECT_JARS[$i]}
			PORT=${PROJECT_PORTS[$i]}
			if [ "$cmd2" == "all" ] || [ "$cmd2" == "$APPNAME" ]; then
				cmd2ok=1
			   psid=$(checkpid $CLASSNAME);
			   if [ $psid -ne 0 ]; then
			      echo -n "Stopping $CLASSNAME ...(pid=$psid) "
			      kill -9 $psid
			      if [ $? -eq 0 ]; then
			         okcount=$(($okcount+1))
			         echo "(pid=$psid,port=$PORT)[OK]"
			      else
			         echo "[Failed]"
			      fi
			 
			      psid=$(checkpid $CLASSNAME);
			      if [ $psid -ne 0 ]; then
			         stop "$cmd2"
			      fi
			   else
			      echo "================================"
			      echo "warn: $CLASSNAME is not running"
			      echo "================================"
			   fi
			fi
		done
		if (($cmd2ok==0));then
			echo "command2: $PROJECT_CMD"
		else
			echo "---------------------------本次共停止:$okcount个服务"
		fi
}
 
###################################
#(函数)检查程序运行状态
#
#说明：
#1. 首先调用checkpid函数
#2. 如果程序已经启动（$psid不等于0），则提示正在运行并表示出pid
#3. 否则，提示程序未运行
###################################
status() {
    local APPNAME=
	local NAME=
	local CLASSNAME=
	local PORT=
	local cmd2="$1"
    local psid=0
	local cmd2ok=0
	echo "===============开始检查程序运行状态..."
	for(( i=0;i<${#PROJECT_APP_NAMES[@]};i++))
		do
			APPNAME=${PROJECT_APP_NAMES[$i]}
			NAME=${PROJECT_NAMES[$i]}
			CLASSNAME=${PROJECT_JARS[$i]}
			PORT=${PROJECT_PORTS[$i]}
			if [ "$cmd2" == "all" ] || [ "$cmd2" == "$APPNAME" ]; then
				cmd2ok=1
				
			   psid=$(checkpid $CLASSNAME);
			    if [ $psid -ne 0 ];  then
			      echo "$NAME is running! (pid=$psid,port=$PORT)"
			   else
			      echo "$NAME is not running"
			   fi
			fi
		done
		if (($cmd2ok==0)); then
			echo "command2: $PROJECT_CMD"
		fi
}
 
###################################
#(函数)打印系统环境参数
###################################
info() {
   echo "System Information:"
   echo "****************************"
   echo `head -n 1 /etc/issue`
   echo `uname -a`
   echo
   echo "JAVA_HOME=$JAVA_HOME"
   echo `$JAVA_HOME/bin/java -version`
   echo
   echo "APP_HOME=$APP_HOME"
   echo "****************************"
}
 
###################################
#读取脚本的第一个参数($1)，进行判断
#参数取值范围：{start|stop|restart|status|info}
#如参数不在指定范围之内，则打印帮助信息
###################################
case "$1" in
   'start')
    start "$2"
	exit 1
      ;;
   'stop')
     stop "$2"
     ;;
   'restart')
     stop "$2"
     start "$2"
     ;;
   'status')
     status "$2"
     ;;
   'info')
     info
     ;;
  *)
     echo "Usage: $0 {start|stop|restart|status|info}"
     echo "Usage: $1 {$PROJECT_CMD}"
     exit 1
esac  
exit 0 